#pragma once

// reflect a type into a string

#include <typeinfo>
#include <string>
#include <type_traits>

#if defined(__GNUC__) || defined(__clang__) && __has_include(<cxxabi.h>)
#include <cxxabi.h>
#include <cstdlib>
#endif

namespace hamster_cpp_demangle
{
    static std::string demangle(const char* name)
    {
        // For MSCV is OK, But Not for GCC and Clang
        // std::string result = name;
        // return result;
        // -------------
        #if defined(__GNUC__) || defined(__clang__) && __has_include(<cxxabi.h>)
        int status;
        char* p = abi::__cxa_demangle(name, 0, 0, &status);
        std::string s = p ? p : name;
        free(p);
        #else
        std::string s = name;
        #endif
        return s;
    }
    
    static std::string demangle(const std::type_info& type)
    {
        return demangle(type.name());
    }

    template<typename T>
    static std::string demangle()
    {
        std::string s = {
            demangle(typeid(std::remove_cv_t<
                     std::remove_reference_t<T>>))
        };
        if(std::is_const_v<std::remove_reference_t<T>>)
        {
            s += " const";
        }
        if(std::is_volatile_v<std::remove_reference_t<T>>)
        {
            s += " volatile";
        }
        if(std::is_lvalue_reference_v<T>)
        {
            s += " &";
        }
        if(std::is_rvalue_reference_v<T>)
        {
            s += " &&";
        }
        return s;
    }
};